<?php

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/**
 * Esta clase contiene las funciones necesaria para escribir/leer en el archivo de configuración de mapcache
 * patron singleton
 * @author mapcache
 */
class ConfigurationManager {

    public static $instance = NULL;
    private $mapcacheXML;
    private $mapcacheXMLQUERY;
    private $regionPopUpAttr;
    private $bboxKeys = array('minX', 'minY', 'maxX', 'maxY');

    public function getBboxKeys() {
        return $this->bboxKeys;
    }

    public function getRegionPopUpAttr() {
        return $this->regionPopUpAttr;
    }

    private function __construct() {
//Todo sacar el harcodeo del filename

        $this->mapcacheXML = new DOMDocument();
        $this->mapcacheXML->load(MAPCACHEXML);
        $this->mapcacheXMLQUERY = new DOMXpath($this->mapcacheXML);
        $this->regionPopUpAttr = array(
            'id' => 'regionSelectAnchor',
            'width' => '700',
            'height' => '500',
            'scrollbars' => 'no',
            'status' => 'yes',
            'resizable' => 'false',
            'screenx' => '\'+((parseInt(screen.width) - 700))+\'',
            'screeny' => 'auto'
        );
    }

    public static function getInstance() {
        if (!isset(self::$instance)) {
            self::$instance = new ConfigurationManager();
        }
        return self::$instance;
    }

    /**
     * Devuelve el path donde se almacena la caché en disco
     * Solo válido si hay configurado un solo tipo de cache con nombre disk y tipo disk
     */
    public function getCachePath() {
        try {
            $path = $this->mapcacheXMLQUERY->query('/mapcache/cache[@name="disk" and @type="disk"]')->item(0);
            return(trim($path->nodeValue));
        } catch (Exception $e) {
            return FALSE;
        }
    }

    /**
     * Devuelve un objeto PHP con los datos del Source
     */
    public function getSourceDataByName($sourceName) {

        $source = $this->mapcacheXMLQUERY->query('/mapcache/source[@name="' . $sourceName . '"]')->item(0);
        return $source === null ? null : simplexml_import_dom($source);
    }

    public function getSupportedGrids() {
        $gridsXML = $this->mapcacheXMLQUERY->query('/mapcache/grid');
        $result = array();
        foreach ($gridsXML as $i => $grid) {
            $result[$i] = $grid->getAttribute('name');
        }
        return $result;
    }

    /**
     * Devuelve un objeto SimpleXMLElement con los datos del SRS configurado
     * @param type $srs Nombre del SRS configurado
     * @return null
     */
    public function getGridData($srs) {
        $gridDataXML = $this->mapcacheXMLQUERY->query('/mapcache/grid[@name="' . $srs . '"]')->item(0);
        $gridDataObj = $gridDataXML == null ? null : simplexml_import_dom($gridDataXML);
        if (isset($gridDataObj)) {

            $srsExtent = explode(' ', trim((String) $gridDataObj->extent));

            $srsExtent = array_combine($this->getBboxKeys(), array_values($srsExtent));
            $data = array(
                'name' => $srs,
                'resolutions' => explode(' ', trim((String) $gridDataObj->resolutions)),
                'extent' => $srsExtent,
                'units' => trim((String) $gridDataObj->units)
            );
            return $data;
        }
        return null;
    }

    /**
     * Devuelve un array de  SimpleXMLElement con los datos de cada SRS configurado
     * 
     * @return null
     */
    public function getSupportedGridsData() {
        $gridsDataXML = $this->mapcacheXMLQUERY->query('/mapcache/grid');
        $gridsData = array();
        if ($gridsDataXML) {
            foreach ($gridsDataXML as $i => $gridDataXML) {
                $gridDataObj = simplexml_import_dom($gridDataXML);
                if ($gridDataObj) {
                    $data = array(
                        'resolutions' => explode(' ', trim((String) $gridDataObj->resolutions)),
                        'extent' => trim((String) $gridDataObj->extent), // 0=> minX, 1=> minY, 2=> maxX, 3=> maxY
                        'units' => trim((String) $gridDataObj->units)
                    );
                    $gridsData[trim((String) $gridDataObj->attributes()->name)] = $data;
                }
            }
            return $gridsData;
        } else {
            return null;
        }
    }

    /**
     * Devuelve un objeto PHP con los datos del TileSet
     */
    public function getConfiguredTileSets() {

        $tilesets = $this->mapcacheXMLQUERY->query('/mapcache/tileset');
        $result = array();
        foreach ($tilesets as $i => $tileset) {
            $tilesetName = $tileset->getAttribute('name');
            $result[$tilesetName] = $tilesetName;
        }
        return $result;
    }

    /**
     * Devuelve un objeto PHP con los datos del TileSet
     */
    public function getTileSetDataByName($tileSetName) {
        $tileset = $this->mapcacheXMLQUERY->query('/mapcache/tileset[@name="' . $tileSetName . '"]')->item(0);

        return $tileset === null ? null : simplexml_import_dom($tileset);
    }

    public function getTilesetGrids($tileSetName) {

        $tileset = $this->mapcacheXMLQUERY->query('/mapcache/tileset[@name="' . $tileSetName . '"]')->item(0);
        if ($tileset !== NULL) {
            $tileset = simplexml_import_dom($tileset);
            $grids = array();
            $count = count($tileset->grid);
            for ($i = 0; $i < $count; $i++) {
                $grids['grids'][(String) $tileset->grid[$i]] = $tileset->grid[$i];
            }
            return $grids;
        }
        return null;
    }

    /**
     * Devuelve un objeto PHP con los datos del TileSet
     */
    public function getAllTilesetsData() {

        $tilesets = $this->mapcacheXMLQUERY->query('/mapcache/tileset');
        $layers = array();
        if (!empty($tilesets)) {
            $i = 0;
            foreach ($tilesets as $key => $tileset) {
                $tilsetObj = simplexml_import_dom($tileset);


                $layers[(String) $tilsetObj->attributes()->name]['grids'] = array();
                $count = count($tilsetObj->grid);
                for ($j = 0; $j < $count; $j++) {
                    $layers[(String) $tilsetObj->attributes()->name]['grids'][(String) $tilsetObj->grid[$j]] = $tilsetObj->grid[$j];
                }
                $i++;
            }

            return $layers;
        } else {
            return null;
        }
    }

    /**
     * Devuelve un array con todos los formatos configurados en mapcache.xml
     * @return Array
     */
    public function getImageFormats() {

        $formats = array();
        $formatsXML = $this->mapcacheXMLQUERY->query('/mapcache/format/@name');
        foreach ($formatsXML as $i => $format) {
            $formats[(String) $format->value] = $format->value;
        }
        return $formats;
    }

    private function createTileset($name, $source, $srsArray, $format, $expires = '300', $autoExpire = '86400',$cache = 'disk', $metatileX = '4', $metatileY = '4', $metabuffer = '10') {
        $tileset = $this->mapcacheXML->createElement('tileset');
        $tileset->setAttribute('name', $name);
        $sourceNode = $this->mapcacheXML->createElement('source', $source);
        $cacheNode = $this->mapcacheXML->createElement('cache', $cache);
        $metatileNode = $this->mapcacheXML->createElement('metatile', $metatileX . ' ' . $metatileY);
        $formatNode = $this->mapcacheXML->createElement('format', $format);
        $metabufferNode = $this->mapcacheXML->createElement('metabuffer', $metabuffer);
        $expiresNode = $this->mapcacheXML->createElement('expires', $expires);
        $autoExpireNode = $this->mapcacheXML->createElement('auto_expire', $autoExpire);
        $tileset->appendChild($sourceNode);
        $tileset->appendChild($cacheNode);
        $tileset->appendChild($metatileNode);
        $tileset->appendChild($formatNode);
        $tileset->appendChild($metabufferNode);
        $tileset->appendChild($expiresNode);
        $tileset->appendChild($autoExpireNode);

        foreach ($srsArray as $srsObject) {

            $gridNode = $this->mapcacheXML->createElement('grid', $srsObject['srs']);
            if ($srsObject['limitBBox']) {
                $gridNode->setAttribute('restricted_extent', $srsObject['restrictedBBox']);
            }
            if ($srsObject['limitZoomLevels']) {
                if ($srsObject['minZoomLevel'])
                    $gridNode->setAttribute('minzoom', $srsObject['minZoomLevel']);
                if ($srsObject['maxZoomLevel'])
                    $gridNode->setAttribute('maxzoom', $srsObject['maxZoomLevel']);
            }
            $tileset->appendChild($gridNode);
        }
        return $tileset;
    }

    private function createSource($name, $url, $layer, $transparentOption, $enableFeature = FALSE, $format = 'image/png', $timeout = '60') {
        $source = $this->mapcacheXML->createElement('source');
        $source->setAttribute('name', $name);
        $source->setAttribute('type', 'wms'); //son simpre wms

        $getmap = $this->mapcacheXML->createElement('getmap');
        $params = $this->mapcacheXML->createElement('params');

        $formatNode = $this->mapcacheXML->createElement('FORMAT', $format);
        $layersNode = $this->mapcacheXML->createElement('LAYERS', $layer);


        $params->appendChild($formatNode);
        $params->appendChild($layersNode);

        if ($transparentOption) {
            $transparentNode = $this->mapcacheXML->createElement('TRANSPARENT', 'TRUE');
            $params->appendChild($transparentNode);
        }
        $getmap->appendChild($params);

        $http = $this->mapcacheXML->createElement('http');

        $urlNode = $this->mapcacheXML->createElement('url', $url);
        $connectionTimeoutNode = $this->mapcacheXML->createElement('connection_timeout', $timeout);
        $http->appendChild($urlNode);
        $http->appendChild($connectionTimeoutNode);

        $source->appendChild($getmap);
        if ($enableFeature) {
            $infoFormats = $this->mapcacheXML->createElement('info_formats', 'text/html,application/vnd.ogc.gml');
            $getFeatureInfo = $this->mapcacheXML->createElement('getfeatureinfo');
            $getFeatureInfoParams = $this->mapcacheXML->createElement('params');
            $queryLayers = $this->mapcacheXML->createElement('QUERY_LAYERS', $layer);
            $getFeatureInfoParams->appendChild($queryLayers);
            $getFeatureInfo->appendChild($infoFormats);
            $getFeatureInfo->appendChild($getFeatureInfoParams);
            $source->appendChild($getFeatureInfo);
        }
        $source->appendChild($http);

        return $source;
    }

    /**
     * Actualiza la configuración de la capa en el servicio WMTS
     * Crea los elementos Source y TileSet en mapcache.xml
     * @param String $tilesetName
     * @param Array $srsArray 
     * @param String $format
     * @param String $metatileX
     * @param String $metatileY
     * @param String $sourceName
     * @param String $sourceURL
     * @param String $layer
     * @param boolean $transparentOption
     */
    public function updateLayer($tilesetName, $srsArray, $format, $sourceName, $sourceURL, $layer, $transparentOption,$enableFeature = FALSE,$expires,$autoExpire) {
        $this->removeLayer($tilesetName, FALSE);
        $this->createLayer($tilesetName, $srsArray, $format, $sourceName, $sourceURL, $layer, $transparentOption,$enableFeature,$expires,$autoExpire);
    }

    /**
     * Configura una nueva capa en el servicio WMTS
     * Crea los elementos Source y TileSet en mapcache.xml
     * @param String $tilesetName
     * @param Array $srsArray 
     * @param String $format
     * @param String $metatileX
     * @param String $metatileY
     * @param String $sourceName
     * @param String $sourceURL
     * @param String $layer
     * @param boolean $transparentOption
     */
    public function createLayer($tilesetName, $srsArray, $format, $sourceName, $sourceURL, $layer, $transparentOption,$enableFeature = FALSE,$expires='300',$autoExpires='86400') {

        try {
            $source = $this->createSource($sourceName, $sourceURL, $layer, $transparentOption,$enableFeature);
            $this->configuereNewSource($source);
            $tileset = $this->createTileset($tilesetName, $sourceName, $srsArray, $format,$expires,$autoExpires);
            $this->configureNewTileset($tileset);
            return TRUE;
        } catch (Exception $e) {
            return FALSE;
        }
    }

    /**
     * Elimina una capa de la configuración de mapcache
     * @param type $tilesetName
     * @param type $sourceName
     * @param type $keepSource por defecto en falso no se elimina el source porque podria utilizarse en otra capa
     */
    public function removeLayer($tilesetName, $keepSource = FALSE) {
        return $this->removeTileset($tilesetName, $keepSource);
    }

    /**
     * Elimina un source
     * @param type $sourceName
     */
    private function removeSource($sourceName) {
        $source = $this->getSourceDataByName($sourceName);
        try {
            if ($source) {
// Antes de borrarlo hay que convertirlo en un DOMElement
                $source = dom_import_simplexml($source);
                $sourceErased = $source->parentNode->removeChild($source);
                $this->mapcacheXML->save('/home/mapcache/mapcache/mapcache.xml');
                return $sourceErased;
            }
        } catch (Exception $e) {

            throw $e;
        }
    }

    /**
     * Elimina un tileset
     * @param type $tilesetName
     */
    private function removeTileset($tilesetName, $keepSource) {
        try {
            $tileset = $this->getTileSetDataByName($tilesetName);
            $sourceName = (String) $tileset->source;
//Por ahora voy a tener siempre 1 Source por cada Tileset
// asi que puedo borrar el source tranquilo
            if (!$keepSource) {

                $this->removeSource($sourceName);
            }
// Antes de borrarlo hay que convertirlo en un DOMElement
            $tileset = dom_import_simplexml($tileset);
            $tilesetErased = $tileset->parentNode->removeChild($tileset);
            $this->mapcacheXML->save('/home/mapcache/mapcache/mapcache.xml');
            return $tilesetErased;
        } catch (Exception $e) {
            return FALSE;
        }
    }

    /**
     * Agrega un nuevo Source en el archivo de configuración de Mapcache
     * @param type $source
     */
    private function configuereNewSource($source) {
        $firtSource = $this->mapcacheXMLQUERY->query('/mapcache/source[1]')->item(0);
//lo inserto al principio de los source
        $firtSource->parentNode->insertBefore($source, $firtSource);
        $this->mapcacheXML->save('/home/mapcache/mapcache/mapcache.xml');
    }

    /**
     * Agrega un nuevo tileset al archivo de configuración de Mapcache
     * @param type $tileset
     */
    private function configureNewTileset($tileset) {
        $firtTileset = $this->mapcacheXMLQUERY->query('/mapcache/tileset[1]')->item(0);
//lo inserto al principio de los tilesets
        $firtTileset->parentNode->insertBefore($tileset, $firtTileset);
        $this->mapcacheXML->save('/home/mapcache/mapcache/mapcache.xml');
    }

    private function existOtherTilesetWithSource($sourceName, $tilesetName) {
        $tilesets = $this->mapcacheXMLQUERY->query('/mapcache/tileset[name != "' . $tilesetName . '"]/source[ . = "' . $sourceName . '"]');
        return !empty($tilesets);
    }

}

?>
